#include "commandlinemgrbase.h"
#include "lib/mem.h"
#include "soc/soc_misc.h"
#include "lib/stringex.h"
#include "os/mutexlocker.h"
#include "commandlinestream.h"
#include "FreeRTOS.h"        
#include "task.h"     
#include "log/logger.h"
#include "visualization/visualosc.h"
#include "lib/misc.h"                  

static const CommandRegisterItem m_cmds[] = {
    DEF_CLI_COMMAND("help", "list all cmd", CommandLineMgrBase::Help),
    DEF_CLI_COMMAND("?", "list all cmd", CommandLineMgrBase::Help),
    DEF_CLI_COMMAND("cls", "clear display", CommandLineMgrBase::Clear),
    DEF_CLI_COMMAND("clear", "clear display", CommandLineMgrBase::Clear),
    DEF_CLI_COMMAND("reset", "reset cpu", CommandLineMgrBase::SystemRest),
    DEF_CLI_COMMAND("temp", "read cpu temperature", CommandLineMgrBase::ReadCpuTemperature),
    DEF_CLI_COMMAND("ps", "list all thread", CommandLineMgrBase::ListThread),
    DEF_CLI_COMMAND("taskst", "get task state", CommandLineMgrBase::ListThreadState),
    DEF_CLI_COMMAND("taskc", "get task count", CommandLineMgrBase::GetOsThreadCount),
    DEF_CLI_COMMAND("mem", "read space ram memory", CommandLineMgrBase::ReadSpaceMem),
    DEF_CLI_COMMAND("cpuid", "read cpuid", CommandLineMgrBase::GetCpuId),
    DEF_CLI_COMMAND("fz", "read flash size", CommandLineMgrBase::GetCpuFlashSize),
    DEF_CLI_COMMAND("freq", "read freq size", CommandLineMgrBase::GetCpuFrequency),
    DEF_CLI_COMMAND("ms", "read cpuid", CommandLineMgrBase::GetCpuCurrentMs),
    DEF_CLI_COMMAND("fwver", "show fw version", CommandLineMgrBase::ShowFWVersion),
    DEF_CLI_COMMAND("log", "log on/off", CommandLineMgrBase::LogSetEnable),
    DEF_CLI_COMMAND("osc", "osc on/off", CommandLineMgrBase::VisualOscSetEnable)
};

CommandLineMgrBase::CommandLineMgrBase()
{
    BuildBuiltinCommand();
}

CommandLineMgrBase::~CommandLineMgrBase()
{
    ClearAllCommand();
}

void CommandLineMgrBase::RegisterCommandTable(const CommandRegisterItem* cmds, int len)
{
    MutexLocker locker(&m_mutex);

    for(int i = 0; i < len; i++)
    {
        auto ptr = (CommandRegisterItem*)&cmds[i];
        m_commandItems[std::string(ptr->Name)] = ptr; 
    }
}

void CommandLineMgrBase::UnRegisterCommand(const std::string &name)
{
    MutexLocker locker(&m_mutex);

    auto it = m_commandItems.find(name);
    if(it != m_commandItems.end())
    {
        m_commandItems.erase(it);
    }
}

void CommandLineMgrBase::ClearAllCommand()
{
    MutexLocker locker(&m_mutex);

    m_commandItems.clear();
}

void CommandLineMgrBase::ProcessCommand(CommandParamItem &item)
{
    MutexLocker locker(&m_mutex);

    item.CommandMgr = this;//attach this
    auto command = FindCommand(item.Name);
    if(command != nullptr && command->Handle != nullptr)
    {
        command->Handle(item);
    }
    else
    {
        item.UnCmd();
    }
}

CommandRegisterItem* CommandLineMgrBase::FindCommand(const std::string &name)
{
    auto it = m_commandItems.find(name);

    return (it != m_commandItems.end())? it->second : nullptr;
}

void CommandLineMgrBase::Help(CommandParamItem &item)
{
    if(item.CommandMgr != nullptr)
    {
        item.CommandMgr->ShowHelpImpl(item);
    }
}

void CommandLineMgrBase::Clear(CommandParamItem &item)
{
    item.Resp = "\033[2J\033[H#";
    item.AckOnlyResp = true;
}

void CommandLineMgrBase::SystemRest(CommandParamItem &item)
{
    SOC_Misc::ResetCpu();
}

void CommandLineMgrBase::ReadCpuTemperature(CommandParamItem &item)
{
    item.Resp = "40";
}

void CommandLineMgrBase::ListThread(CommandParamItem &item)
{
#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )    
    char *buffer = (char*)MemMalloc(512);
    vTaskList(buffer);
    item.Resp = "\r\nname\t\tstate\tprio\trstack\tid\r\n" + std::string(buffer);
    MemFree(buffer);
#endif 
}

void CommandLineMgrBase::ListThreadState(CommandParamItem &item)
{
#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )    
    char *buffer = (char*)MemMalloc(512);
    vTaskGetRunTimeStats((char *) &buffer);
    item.Resp = "\r\nname\t\tusecount\tusage\r\n" + std::string(buffer);
    MemFree(buffer);
#endif
}

void CommandLineMgrBase::GetOsThreadCount(CommandParamItem &item)
{
    item.Resp = StringEx::Format("%d", uxTaskGetNumberOfTasks());
}

void CommandLineMgrBase::ReadSpaceMem(CommandParamItem &item)
{
    item.Resp = StringEx::Format("%d", MemGetFree());
}

void CommandLineMgrBase::GetCpuId(CommandParamItem &item)
{
    item.Resp = SOC_Misc::GetCpuUID();
}

void CommandLineMgrBase::GetCpuFlashSize(CommandParamItem &item)
{
    item.Resp = StringEx::Format("%d", SOC_Misc::GetFlashSize());
}

void CommandLineMgrBase::GetCpuFrequency(CommandParamItem &item)
{
    item.Resp = StringEx::Format("%d", SOC_Misc::GetCpuFrequency());
}

void CommandLineMgrBase::GetCpuCurrentMs(CommandParamItem &item)
{
    item.Resp = StringEx::Format("%d", SOC_Misc::CurrentCpuMilliseconds());
}

void CommandLineMgrBase::ShowFWVersion(CommandParamItem &item)
{
    item.Resp = FW_VERSION;
}

void CommandLineMgrBase::LogSetEnable(CommandParamItem &item)
{
    if(item.Params.size() == 0)
        return;
        
    bool en = item.Params[0] == "1";
    Logger::Instance().Configuration.LogEnable = en;
    item.Resp = en? "log on" : "log off";
}

void CommandLineMgrBase::VisualOscSetEnable(CommandParamItem &item)
{
    if(item.Params.size() == 0)
        return;

    bool en = item.Params[0] == "1";
    VisualOsc::SetEnabled(en);
    item.Resp = en? "visual osc on" : "visual osc off";
}

void CommandLineMgrBase::BuildBuiltinCommand()
{
    RegisterCommandTable(m_cmds, SIZEOF_ARRAY(m_cmds));
}

void CommandLineMgrBase::ShowHelpImpl(CommandParamItem &item)
{
    if(item.Stream == nullptr)
        return;

    //因为help 可能响应字符串比较长，所以这里分包写入
    if(item.Params.size() > 0)
    {
        auto command = FindCommand(item.Params[0]);
        if(command)
        {
            std::string str;
            str.append(command->Name).append("->").append(command->Help);
            item.Stream->SendResp(str.c_str());
        }
        else
        {
            item.Stream->SendResp(nullptr);
        }
    }
    else 
    {
        item.Stream->SendTo(NEWLINE);
        item.Stream->SendTo(NEWLINE);

        for(auto &c : m_commandItems)
        {
            std::string str;
            str.append(c.second->Name);
            str.append("->");
            str.append(c.second->Help);
            str.append(NEWLINE);
            str.append(NEWLINE);
            item.Stream->SendTo(str.c_str());
        }
        
        item.Stream->SendTo(NEWLINE"#");
    }

    item.CancelSendTo = true;
}
